package com.lai.hospital.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lai.hospital.dao.*;
import com.lai.hospital.dto.*;
import com.lai.hospital.entity.*;
import com.lai.hospital.enums.AppointmentStatusEnum;
import com.lai.hospital.enums.FilePathEnum;
import com.lai.hospital.service.DoctorService;
import com.lai.hospital.service.PatientService;
import com.lai.hospital.strategy.context.UploadStrategyContext;
import com.lai.hospital.utils.BeanCopyUtils;
import com.lai.hospital.utils.PageUtils;
import com.lai.hospital.utils.UserUtils;
import com.lai.hospital.vo.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.stream.Collectors;


/**
 * @author : lai
 * @version V1.0
 * @Project: hospital-springboot
 * @Package com.lai.hospital.service.impl
 * @Description: 医生服务
 * @date Date : 2022年11月24日 21:15
 */
@Service
public class DoctorServiceImpl extends ServiceImpl<DoctorDao, Doctor> implements DoctorService {

    @Autowired
    private DoctorDao doctorDao;

    @Autowired
    private DepartmentDao departmentDao;

    @Autowired
    private AppointmentDao appointmentDao;

    @Autowired
    private UploadStrategyContext uploadStrategyContext;

    @Autowired
    private DoctorScheduleDao doctorScheduleDao;

    @Autowired
    private PatientService patientService;
    @Autowired
    private PatientDao patientDao;

    @Override
    public PageResult<DoctorDTO> listDoctor(ConditionVO conditionVO) {
        Page<Doctor> page = new Page<>(PageUtils.getCurrent(), PageUtils.getSize());
        Integer count;
        //查询科室信息
        List<Department> departmentList = departmentDao.selectList(null);
        //转换成Map   根据id获取科室名称
        Map<Integer, String> departmentMap = departmentList.stream()
                .collect(Collectors.toMap(Department::getId, Department::getDepartmentName));
        //获取医师列表
        //拼装查询条件
        LambdaQueryWrapper<Doctor> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank(conditionVO.getKeywords())) {
            lambdaQueryWrapper.like(Doctor::getDoctorName, conditionVO.getKeywords());
        }
        if (StringUtils.isNotBlank(conditionVO.getProfessional())) {
            lambdaQueryWrapper.eq(Doctor::getProfessional, conditionVO.getProfessional());
        }
        if (Objects.nonNull(conditionVO.getDepartmentId())) {
            lambdaQueryWrapper.eq(Doctor::getDepartmentId, conditionVO.getDepartmentId());
        }
        lambdaQueryWrapper.orderByAsc(Doctor::getId);
        //分页查询
        Page<Doctor> doctorPage = this.page(page, lambdaQueryWrapper);
        List<DoctorDTO> doctorDTOList = BeanCopyUtils.copyList(doctorPage.getRecords(), DoctorDTO.class);
        //设置科室名
        List<DoctorDTO> dtoList = doctorDTOList.stream().map(item -> {
            item.setDepartmentName(departmentMap.get(item.getDepartmentId()));
            return item;
        }).collect(Collectors.toList());

        //设置列表总数   有条件查询时doctorDTOList的大小
        if (StringUtils.isNotBlank(conditionVO.getKeywords()) || StringUtils.isNotBlank(conditionVO.getProfessional())
                || Objects.nonNull(conditionVO.getDepartmentId())) {
            count = doctorDTOList.size();
        } else {//无条件查询时获取全部数据
            count = doctorDao.selectCount(null);
        }
        return new PageResult<>(dtoList, count);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveOrUpdateDoctor(DoctorVO doctorVO) {
        Doctor doctor = BeanCopyUtils.copyObject(doctorVO, Doctor.class);
        doctor.setDepartmentId(doctorVO.getDepartmentId());
        this.saveOrUpdate(doctor);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String updateDoctorAvatar(MultipartFile file) {
        return uploadStrategyContext.executeUploadStrategy(file, FilePathEnum.DOCTOR.getPath());
    }

    @Override
    public void deleteDoctor(Integer id) {
        doctorDao.deleteById(id);
    }

    @Override
    public List<RecommendedDoctorDTO> listRecommendedDoctor(Integer userId) {
        // 查询最近2次的预约信息，查看有哪些科室，推荐4位属于这些科室的医生
        QueryWrapper<Appointment> queryWrapper = new QueryWrapper<>();
        if(Objects.nonNull(userId)){
            queryWrapper.eq("user_id", userId);
        }
        queryWrapper.eq("appointment_status", AppointmentStatusEnum.COMPLETE.getStatus());
        queryWrapper.orderByDesc("create_time");
        queryWrapper.last("limit 2");
        List<Appointment> appointments = appointmentDao.selectList(queryWrapper);
        // 科室id 集合
        Set<Integer> departmentIdList = appointments.stream().map(Appointment::getDepartmentId).collect(Collectors.toSet());

        // 通过科室id查询医生
        // 最近预约有两个科室，则每个科室抽两位医生，一个科室则抽4位医生
        ArrayList<RecommendedDoctorDTO> doctorDTOS = new ArrayList<>();
        int num = departmentIdList.size() > 1 ? 2 : 3;
        for (Integer departmentId : departmentIdList) {
            List<Doctor> doctors = doctorDao.selectList(new QueryWrapper<Doctor>().eq("department_id", departmentId));
            if (doctors.size() == 0) {
                continue;
            }
            //副科室
            Department department = departmentDao.selectById(departmentId);
            //主科室
            Department parentDepartment = departmentDao.selectById(department.getParentId());
            for (int i = 0; i < num; i++) {
                if (doctors.size() == 0) {
                    break;
                }
                Random r = new Random();
                Doctor doctor = doctors.remove(r.nextInt(doctors.size()));
                RecommendedDoctorDTO doctorDTO = new RecommendedDoctorDTO();
                doctorDTO.setAvatar(doctor.getAvatar());
                doctorDTO.setId(doctor.getId());
                doctorDTO.setDoctorName(doctor.getDoctorName());
                doctorDTO.setDepartmentName(parentDepartment.getDepartmentName());
                doctorDTOS.add(doctorDTO);
            }
        }
        return doctorDTOS;
    }

    @Override
    public List<MyDoctorDTO> listMyDoctor(Integer userId, Integer num) {
        return doctorDao.selectMyDoctor(userId, num);
    }

    @Override
    public DoctorDTO getDoctorInfoById(Integer id) {

        Doctor doctor = doctorDao.selectById(id);
        DoctorDTO doctorDTO = BeanCopyUtils.copyObject(doctor, DoctorDTO.class);
        //查询科室信息
        Department department = departmentDao.selectById(doctor.getDepartmentId());
        Department parentDepartment = departmentDao.selectById(department.getParentId());
        doctorDTO.setDepartmentName(parentDepartment.getDepartmentName());
        return doctorDTO;
    }

    @Override
    public List<Doctor> getDoctorByDepartmentAndScheduleDate(DepartmentDoctorScheduleVO departmentDoctorScheduleVO) {
        String beginOfDayFormat;
        String endOfDayFormat;
        if(Objects.nonNull(departmentDoctorScheduleVO.getAppointmentTime())){
            //用户选择日期开始时间
            //DateTime beginOfDay = DateUtil.beginOfDay(departmentDoctorScheduleVO.getAppointmentTime());
            //beginOfDayFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd");
            //用户选择日期结束时间
            //DateTime endOfDay = DateUtil.endOfDay(departmentDoctorScheduleVO.getAppointmentTime());
            //endOfDayFormat = DateUtil.format(endOfDay, "yyyy-MM-dd");
            beginOfDayFormat = departmentDoctorScheduleVO.getAppointmentTime();
            endOfDayFormat = departmentDoctorScheduleVO.getAppointmentTime();
        }else{
            //当日开始时间
            DateTime beginOfDay = DateUtil.beginOfDay(DateUtil.date());
            beginOfDayFormat = DateUtil.format(beginOfDay, "yyyy-MM-dd");
            //当日结束时间
            DateTime endOfDay = DateUtil.endOfDay(DateUtil.date());
            endOfDayFormat = DateUtil.format(endOfDay, "yyyy-MM-dd");
        }
        //查询出 日开始时间 日结束时间之间得医生排班信息
        List<DoctorSchedule> doctorScheduleList = doctorScheduleDao.selectList(new LambdaQueryWrapper<DoctorSchedule>()
                .eq(DoctorSchedule::getDepartmentId, departmentDoctorScheduleVO.getDepartmentId())
                .ge(DoctorSchedule::getScheduleTime, beginOfDayFormat)
                .le(DoctorSchedule::getScheduleTime, endOfDayFormat));
        //获取其中的医生id
        List<Integer> doctorScheduleIds = doctorScheduleList.stream().map(DoctorSchedule::getDoctorId).collect(Collectors.toList());
        //批量查询医生
        if(doctorScheduleIds.size() > 0 && Objects.nonNull(doctorScheduleIds)){
            List<Doctor> doctorList = doctorDao.selectBatchIds(doctorScheduleIds);
            return doctorList;
        }
        return null;
    }
    @Override
    public ConfirmAppointmentDTO confirmAppointment(AppointmentPreviewVO appointmentPreviewVO) {
        Patient patient;
        if(Objects.nonNull(appointmentPreviewVO.getPatientId())){
            patient = patientDao.selectById(appointmentPreviewVO.getPatientId());
        }else{
            patient = patientService.getUserDefaultPatient();
        }
        ConfirmAppointmentDTO confirmAppointmentDTO = doctorDao.confirmAppointment(appointmentPreviewVO.getDoctorId());
        confirmAppointmentDTO.setPatientId(patient.getId());
        confirmAppointmentDTO.setPatientName(patient.getPatientName());
        confirmAppointmentDTO.setIdNumber(patient.getIdNumber());
        if(StringUtils.isBlank(appointmentPreviewVO.getAppointmentTime()) || Objects.isNull(appointmentPreviewVO.getAppointmentTime())
                || "null".equals(appointmentPreviewVO.getAppointmentTime())){
            confirmAppointmentDTO.setAppointmentTime(DateUtil.format(DateUtil.date(), "yyyy-MM-dd"));
        }else{
            confirmAppointmentDTO.setAppointmentTime(appointmentPreviewVO.getAppointmentTime());
        }
        return confirmAppointmentDTO;
    }

    @Override
    public Doctor doctorInfo() {
        return doctorDao.selectOne(new LambdaQueryWrapper<Doctor>()
                .eq(Doctor::getUserAuthId, UserUtils.getLoginUser().getId()));
    }
}
